package cim3d;

import _61970.core.Substation;
import com.ht.aems.util.RtDataBuffer;
import com.htgraph.util.GraphLoader;
import com.mxgraph.io.mxCodec;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.sun.opengl.util.FPSAnimator;
import org.w3c.dom.Document;

import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStream;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;


/**
 * Created by IntelliJ IDEA.
 *
 * @author Dong Shufeng
 *         Date: 2009-6-19
 */
public class GLManager {
    public static final int FLY_TO_TIME = 50;
    public static final int FLY_TO_PERIOD = 20;
    Preferences prefs;
    GLCanvas gLCanvas;
    private Map<String, mxGraphModel> docs = new HashMap<String, mxGraphModel>();
    private Map<String, ViewerData3D> preference = new HashMap<String, ViewerData3D>();
    private GraphDisplayer displayer = new GraphDisplayer();
    boolean isLineNumShowing = false;
    boolean isAreaMWSpareShowing = false;
    boolean isAreaMVARSpareShowing = false;
    boolean isPowerflowShowing = false;
    boolean isGenPowerShowing = false;
    FPSAnimator animator = new FPSAnimator(15, true);
    RtDataBuffer dataBuffer;
    ActionListener topoChangeListener;

    public RtDataBuffer getDataBuffer() {
        return dataBuffer;
    }

    public void setDataBuffer(RtDataBuffer dataBuffer) {
        this.dataBuffer = dataBuffer;
        if (displayer.getRenders() == null)
            return;
        for (Renderer3D[] renderers : displayer.getRenders().values())
            for (Renderer3D renderer : renderers)
                if (renderer instanceof SlpRenderer3D) {
                    SlpRenderer3D renderer3D = (SlpRenderer3D) renderer;
                    renderer3D.setDataBuffer(dataBuffer);
                }
    }

    public GLManager() {
        initial();
        //refresh();
    }


    public GLManager(Map<String, mxGraphModel> docs) {
        initial();
        setDocs(docs);
    }

    public void setDocs(Map<String, mxGraphModel> docs) {
        this.docs = docs;
        for (String key : docs.keySet()) {
            ViewerData3D viewerData = new ViewerData3D();
            viewerData.setXp(prefs.getDouble(key + ":xp", 0));
            viewerData.setYp(prefs.getDouble(key + ":yp", 0));
            viewerData.setZp(prefs.getDouble(key + ":zp", 300));
            viewerData.setXrot(prefs.getDouble(key + ":xrot", -30));//todo:
            viewerData.setZrot(prefs.getDouble(key + ":zrot", 0));
            viewerData.setWindowWidth(prefs.getDouble(key + ":width", 800));
            viewerData.setWindowHeight(prefs.getDouble(key + ":height", 600));
            preference.put(key, viewerData);
        }
    }

    public void setTopoChangeListener(ActionListener topoChangeListener) {
        this.topoChangeListener = topoChangeListener;
    }

    public void refresh(String key, InputStream stream) {
        try {
            mxGraphModel doc = new mxGraphModel();
            Document document = GraphLoader.parse(stream);
            mxCodec codec = new mxCodec(document);
            codec.decode(document.getDocumentElement(), doc);
            System.out.println("GLManager Load... " + key);
            docs.put(key, doc);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        setDocs(docs);
    }

    public void startAnimator() {
        animator.add(gLCanvas);
        animator.start();
    }

    public void stopAnimator() {
        animator.stop();
    }

    private void initial() {
        prefs = Preferences.userRoot().node(this.getClass().getName());

        GLCapabilities glCap = new GLCapabilities();
        glCap.setSampleBuffers(true);
        glCap.setNumSamples(5);
        gLCanvas = new GLCanvas(glCap);
        glCap.setDoubleBuffered(true);
        gLCanvas.setPreferredSize(new Dimension(800, 600));
        gLCanvas.setSize(gLCanvas.getPreferredSize());

        gLCanvas.addGLEventListener(displayer);
        gLCanvas.addKeyListener(new DefaultKeyListener(displayer));
        DefaultMouseListener defaultMouseListener = new DefaultMouseListener(displayer);

        gLCanvas.addMouseListener(defaultMouseListener);
        gLCanvas.addMouseMotionListener(defaultMouseListener);
        gLCanvas.addMouseWheelListener(defaultMouseListener);
        gLCanvas.addGLEventListener(defaultMouseListener);

        defaultMouseListener.setSelectionListener(new GLSelectionListener() {
            public void processHits(int hits, IntBuffer buffer, int pickingStatus) {
                Object obj = displayer.getGraph().getLayers().get(0).getGraphElements().get(buffer.get(3));
                if (obj == null)
                    return;
                if (obj instanceof mxCell) {
                    mxCell array = (mxCell) obj;
                    String type = array.getAttribute(CIMViewConst.ATTRI_DEV_TYPE);
                    if (type != null && type.equals(CIMViewConst.DEV_TYPE_REGION)) {
                        String name = array.getAttribute(CIMViewConst.ATTRI_DEV_NAME);
                        changeTopo(name);
                    } else {
                        double deltaX = -displayer.getViewerData().getXp() - displayer.getViewerData().getWindowWidth() / 2.0 + array.getGeometry().getCenterX();
                        double deltaY = -displayer.getViewerData().getYp() + displayer.getViewerData().getWindowHeight() / 2.0 - array.getGeometry().getCenterY();
                        new FlyToThread(deltaX, deltaY).start();
                    }
                }
            }
        });
    }

    public void flyTo(String cellId) {
        if (cellId == null) return;
        for (String key : docs.keySet()) {
            Object obj = docs.get(key).getCell(cellId);
            if (obj != null && (obj instanceof mxCell)) {
                mxCell e = (mxCell) obj;
                changeTopo(key);
                double deltaX = -displayer.getViewerData().getXp() - displayer.getViewerData().getWindowWidth() / 2.0 + e.getGeometry().getCenterX();
                double deltaY = -displayer.getViewerData().getYp() + displayer.getViewerData().getWindowHeight() / 2.0 - e.getGeometry().getCenterY();
                new FlyToThread(deltaX, deltaY).start();
                break;
            }
        }
    }

    public void changeTopo(String key) {
        if (!docs.containsKey(key))
            return;
        System.out.println("GLManager change view to: " + key);
        mxGraphModel doc = docs.get(key);

        GraphInfo graph = new GraphInfo();
        graph.setGraphName(key);
        graph.setLayers(new ArrayList<GraphLayerInfo>());

        Map<GraphLayerInfo, Renderer3D[]> map = new HashMap<GraphLayerInfo, Renderer3D[]>();
        GraphLayerInfo layer = new GraphLayerInfo();
        layer.setName("");
        layer.setGraphElements(new ArrayList(doc.getCells().values()));
        layer.setDataUpdated(true);
        map.put(layer, new Renderer3D[]{new SlpRenderer3D()});
        graph.addLayer(layer);
        for (GraphLayerInfo g : map.keySet())
            for (Renderer3D renderer : map.get(g))
                if (renderer instanceof SlpRenderer3D) {
                    ((SlpRenderer3D) renderer).setDisplayer(displayer);
                    ((SlpRenderer3D) renderer).setDataBuffer(dataBuffer);
                }

        displayer.setViewerData(preference.get(key));
        displayer.setGraph(graph);
        displayer.setRenders(map);

        initialSlpRenderer();
        if (topoChangeListener != null)
            topoChangeListener.actionPerformed(new ActionEvent(this, -1, key));
    }

    public GLCanvas getGLCanvas() {
        return gLCanvas;
    }

    private void initialSlpRenderer() {
        if (displayer.getRenders() == null)
            return;
        for (Renderer3D[] renderers : displayer.getRenders().values())
            for (Renderer3D renderer : renderers)
                if (renderer instanceof SlpRenderer3D) {
                    SlpRenderer3D renderer3D = (SlpRenderer3D) renderer;
                    renderer3D.setAreaMWSpareShowing(isAreaMWSpareShowing);
                    renderer3D.setAreaMVARSpareShowing(isAreaMVARSpareShowing);
                    renderer3D.setPowerflowShowing(isPowerflowShowing);
                    renderer3D.setLineNumShowing(isLineNumShowing);
                    renderer3D.setGenPowerShowing(isGenPowerShowing);
                }
    }


    public void setLineNumShowing(boolean lineNumShowing) {
        isLineNumShowing = lineNumShowing;
        initialSlpRenderer();
    }

    public void setAreaMWSpareShowing(boolean areaMWSpareShowing) {
        isAreaMWSpareShowing = areaMWSpareShowing;
        initialSlpRenderer();
    }

    public void setPowerflowShowing(boolean powerflowShowing) {
        isPowerflowShowing = powerflowShowing;
        initialSlpRenderer();
    }

    public void setGenPowerShowing(boolean genPowerShowing) {
        isGenPowerShowing = genPowerShowing;
        initialSlpRenderer();
    }


    public boolean isAreaMVARSpareShowing() {
        return isAreaMVARSpareShowing;
    }

    public void setAreaMVARSpareShowing(boolean areaMVARSpareShowing) {
        isAreaMVARSpareShowing = areaMVARSpareShowing;
        initialSlpRenderer();
    }

    public GraphDisplayer getDisplayer() {
        return displayer;
    }

    public void setDisplayer(GraphDisplayer displayer) {
        this.displayer = displayer;
    }

    public void savePreference() {
        for (String key : preference.keySet()) {
            prefs.putDouble(key + ":xp", preference.get(key).getXp());
            prefs.putDouble(key + ":yp", preference.get(key).getYp());
            prefs.putDouble(key + ":zp", preference.get(key).getZp());
            prefs.putDouble(key + ":xrot", preference.get(key).getXrot());
            prefs.putDouble(key + ":zrot", preference.get(key).getZrot());
            prefs.putDouble(key + ":width", preference.get(key).getWindowWidth());
            prefs.putDouble(key + ":height", preference.get(key).getWindowHeight());
        }
    }


    public Map<String, mxGraphModel> getDocs() {
        return docs;
    }

    class FlyToThread extends Thread {
        double deltaX;
        double deltaY;

        public FlyToThread(double deltaX, double deltaY) {
            this.deltaX = deltaX;
            this.deltaY = deltaY;
        }

        @Override
        public void run() {
            for (int i = 0; i < FLY_TO_TIME; i++) {
                displayer.getViewerData().setXp(displayer.getViewerData().getXp() + (deltaX) / FLY_TO_TIME);
                displayer.getViewerData().setYp(displayer.getViewerData().getYp() + (deltaY) / FLY_TO_TIME);
                try {
                    sleep(FLY_TO_PERIOD);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
